#include "scheduler.hpp"
#include "fiber.hpp"
#include "hook.hpp"

namespace monsoon {
// 当前线程的调度器，同一调度器下的所有线程共享同一调度器实例 （线程级调度器）
static thread_local Scheduler *cur_scheduler = nullptr;
// 当前线程的调度协程，每个线程一个 (协程级调度器)
static thread_local Fiber *cur_scheduler_fiber = nullptr;

const std::string LOG_HEAD = "[scheduler] ";

Scheduler::Scheduler(size_t threads, bool use_caller, const std::string &name) {
  CondPanic(threads > 0, "threads <= 0");

  isUseCaller_ = use_caller;
  name_ = name;

  // use_caller:是否将当前线程也作为被调度线程
  if (use_caller) {
    std::cout << LOG_HEAD << "current thread as called thread" << std::endl;
    // 总线程数减1
    --threads;
    // 初始化caller线程的主协程
    Fiber::GetThis();
    std::cout << LOG_HEAD << "init caller thread's main fiber success" << std::endl;
    CondPanic(GetThis() == nullptr, "GetThis err:cur scheduler is not nullptr");
    // 设置当前线程为调度器线程（caller thread）
    cur_scheduler = this;
    // 初始化当前线程的调度协程 （该线程不会被调度器带哦都），调度结束后，返回主协程
    rootFiber_.reset(new Fiber(std::bind(&Scheduler::run, this), 0, false));
    std::cout << LOG_HEAD << "init caller thread's caller fiber success" << std::endl;

    Thread::SetName(name_);
    cur_scheduler_fiber = rootFiber_.get();
    rootThread_ = GetThreadId();
    threadIds_.push_back(rootThread_);
  } else {
    rootThread_ = -1;
  }
  threadCnt_ = threads;
  std::cout << "-------scheduler init success-------" << std::endl;
}

Scheduler *Scheduler::GetThis() { return cur_scheduler; }
Fiber *Scheduler::GetMainFiber() { return cur_scheduler_fiber; }
void Scheduler::setThis() { cur_scheduler = this; }
Scheduler::~Scheduler() {
  CondPanic(isStopped_, "isstopped is false");
  if (GetThis() == this) {
    cur_scheduler = nullptr;
  }
}

// 调度器启动
// 初始化调度线程池
void Scheduler::start() {
  std::cout << LOG_HEAD << "scheduler start" << std::endl;
  Mutex::Lock lock(mutex_);
  if (isStopped_) {
    std::cout << "scheduler has stopped" << std::endl;
    return;
  }
  CondPanic(threadPool_.empty(), "thread pool is not empty");
  threadPool_.resize(threadCnt_);
  for (size_t i = 0; i < threadCnt_; i++) {
    threadPool_[i].reset(new Thread(std::bind(&Scheduler::run, this), name_ + "_" + std::to_string(i)));
    threadIds_.push_back(threadPool_[i]->getId());
  }
}

// 调度协程
void Scheduler::run() {
  std::cout << LOG_HEAD << "begin run" << std::endl;
  set_hook_enable(true);
  setThis();
  if (GetThreadId() != rootThread_) {
    // 如果当前线程不是caller线程，则初始化该线程的调度协程
    cur_scheduler_fiber = Fiber::GetThis().get();
  }

  // 创建idle协程
  Fiber::ptr idleFiber(new Fiber(std::bind(&Scheduler::idle, this)));
  Fiber::ptr cbFiber;

  SchedulerTask task;
  while (true) {
    task.reset();
    // 是否通知其他线程进行任务调度
    bool tickle_me = false;
    {
      Mutex::Lock lock(mutex_);
      auto it = tasks_.begin();
      while (it != tasks_.end()) {
        // 发现已经指定调度线程，但是不是在当前线程进行调度
        // 需要通知其他线程进行调度，并跳过当前任务
        if (it->thread_ != -1 && it->thread_ != GetThreadId()) {
          ++it;
          tickle_me = true;
          continue;
        }
        CondPanic(it->fiber_ || it->cb_, "task is nullptr");
        if (it->fiber_) {
          CondPanic(it->fiber_->getState() == Fiber::READY, "fiber task state error");
        }
        // 找到一个可进行任务，准备开始调度，从任务队列取出，活动线程加1
        task = *it;
        tasks_.erase(it++);
        ++activeThreadCnt_;
        break;
      }
      // 当前线程拿出一个任务后，同时任务队列不空，那么告诉其他线程
      tickle_me |= (it != tasks_.end());
    }
    if (tickle_me) {
      tickle();
    }

    if (task.fiber_) {
      // 开始执行 协程任务
      task.fiber_->resume();
      // 执行结束
      --activeThreadCnt_;
      task.reset();
    } else if (task.cb_) {
      if (cbFiber) {
        cbFiber->reset(task.cb_);
      } else {
        cbFiber.reset(new Fiber(task.cb_));
      }
      task.reset();
      cbFiber->resume();
      --activeThreadCnt_;
      cbFiber.reset();
    } else {
      // 任务队列为空
      if (idleFiber->getState() == Fiber::TERM) {
        std::cout << "idle fiber term" << std::endl;
        break;
      }
      // idle协程不断空轮转
      ++idleThreadCnt_;
      idleFiber->resume();
      --idleThreadCnt_;
    }
  }
  std::cout << "run exit" << std::endl;
}

void Scheduler::tickle() { std::cout << "tickle" << std::endl; }

bool Scheduler::stopping() {
  Mutex::Lock lock(mutex_);
  return isStopped_ && tasks_.empty() && activeThreadCnt_ == 0;
}

void Scheduler::idle() {
  while (!stopping()) {
    Fiber::GetThis()->yield();
  }
}

// 使用caller线程，则调度线程依赖stop()来执行caller线程的调度协程
// 不使用caller线程，只用caller线程去调度，则调度器真正开始执行的位置是stop()
void Scheduler::stop() {
  std::cout << LOG_HEAD << "stop" << std::endl;
  if (stopping()) {
    return;
  }
  isStopped_ = true;

  // stop指令只能由caller线程发起
  if (isUseCaller_) {
    CondPanic(GetThis() == this, "cur thread is not caller thread");
  } else {
    CondPanic(GetThis() != this, "cur thread is caller thread");
  }

  for (size_t i = 0; i < threadCnt_; i++) {
    tickle();
  }
  if (rootFiber_) {
    tickle();
  }

  // 在user_caller情况下，调度器协程（rootFiber）结束后，应该返回caller协程
  if (rootFiber_) {
    // 切换到调度协程，开始调度
    rootFiber_->resume();
    std::cout << "root fiber end" << std::endl;
  }

  std::vector<Thread::ptr> threads;
  {
    Mutex::Lock lock(mutex_);
    threads.swap(threadPool_);
  }
  for (auto &i : threads) {
    i->join();
  }
}

}  // namespace monsoon
/*
逐行详解（面向协程入门者）：scheduler.cpp
说明：以下注释按“代码出现顺序”逐条解释每一行/语句的作用，不改变任何逻辑，仅作学习辅助。

[1]  #include "scheduler.hpp" —— 引入调度器类的声明（类接口、成员变量/函数原型等）。
[2]  #include "fiber.hpp"      —— 引入 Fiber（协程）类的声明，调度器需要创建/切换协程。
[3]  #include "hook.hpp"       —— 引入 Hook 子系统开关（把阻塞 I/O 转为“协程挂起 + 事件唤醒”）。

[5]  namespace monsoon {        —— 进入项目的命名空间 monsoon，避免与其他库符号冲突。
[6]  static thread_local Scheduler *cur_scheduler = nullptr;  —— 线程局部变量：记录“当前线程绑定的调度器实例”。
[7]  static thread_local Fiber     *cur_scheduler_fiber = nullptr; —— 线程局部变量：记录“当前线程的调度协程（主 fiber/调度主纤程）”。
      说明：N→M 调度模型下，每个工作线程都需要有一个“调度主协程”，用于在任务协程之间切换；这两个指针帮助在任何地方快速取到“当前调度器/当前调度协程”。

[9]  const std::string LOG_HEAD = "[scheduler] "; —— 统一的日志前缀，便于在输出中定位模块来源。

[11] Scheduler::Scheduler(size_t threads, bool use_caller, const std::string &name) { —— 调度器构造函数：配置线程数、是否把当前线程也纳入调度（use_caller）、名称。
[12]   CondPanic(threads > 0, "threads <= 0"); —— 断言线程数要大于 0（条件不满足时触发 panic/异常）。
[14]   isUseCaller_ = use_caller; —— 记录“是否让当前线程也参与调度”。
[15]   name_ = name;              —— 记录调度器名称（用于线程命名/日志）。

[17]   if (use_caller) {          —— 如果把“当前线程（caller）”也作为调度线程使用：
[19]     打印日志，标识当前线程会加入调度。
[20]     --threads;               —— 因为 caller 线程本身会参与调度，所以剩余要新建的工作线程数量减 1。
[21]     Fiber::GetThis();        —— 在当前线程上初始化“主协程”（若不存在则创建），作为协程切换的落点。
[22]     打印日志，说明 caller 线程的主协程初始化成功。
[23]     CondPanic(GetThis() == nullptr, "GetThis err:cur scheduler is not nullptr"); —— 期望当前线程尚未绑定调度器（这里的断言语义依赖 CondPanic 实现）。
[24]     cur_scheduler = this;    —— 把当前线程的“当前调度器指针”绑定为本实例。
[25]     rootFiber_.reset(new Fiber(std::bind(&Scheduler::run, this), 0, false)); —— 为当前线程创建“调度协程（rootFiber）”，入口函数是 Scheduler::run。
         说明：use_caller 模式下，caller 线程本身不另开系统线程，而是在本线程内以一个“协程（rootFiber）”的方式运行 run() 调度循环；
               调度结束时再切回到 caller 的主协程。
[26]     打印日志，说明 caller 线程的“调度协程”初始化成功。
[27]     Thread::SetName(name_);  —— 设置当前线程的可读名称，便于调试与日志定位。
[28]     cur_scheduler_fiber = rootFiber_.get(); —— 记录“当前线程的调度协程指针”。
[29]     rootThread_ = GetThreadId(); —— 记录当前线程的线程 ID，后续用于识别“caller 线程”。
[30]     threadIds_.push_back(rootThread_); —— 把 caller 线程也加入“调度线程 ID 列表”。
[31]   } else {
[32]     rootThread_ = -1;        —— 不使用 caller 线程：用 -1 表示没有 caller 线程参与。
[33]   }
[34]   threadCnt_ = threads;      —— 记录需要创建的“工作线程数量”（不含 caller 线程）。
[35]   打印“scheduler init success”日志，表示调度器构造完成。
[36] } —— 构造函数结束。

[38] Scheduler *Scheduler::GetThis() { return cur_scheduler; } —— 取出“当前线程绑定的调度器实例”。
[39] Fiber     *Scheduler::GetMainFiber() { return cur_scheduler_fiber; } —— 取出“当前线程的调度协程”。
[40] void Scheduler::setThis() { cur_scheduler = this; } —— 把“当前线程的调度器”设置为 this（在工作线程入口中调用）。

[41] Scheduler::~Scheduler() {   —— 析构函数：清理线程局部绑定。
[42]   CondPanic(isStopped_, "isstopped is false"); —— 要求只有在 stop 之后才能析构（避免运行中被销毁）。
[43]   if (GetThis() == this) {   —— 如果当前线程局部记录的调度器正是 this：
[44]     cur_scheduler = nullptr; —— 清空线程局部的调度器指针。
[45]   }
[46] }

[54] void Scheduler::start() {   —— 启动调度器：根据 threadCnt_ 创建工作线程，并让它们运行 run() 调度循环。
[55]   打印“scheduler start”日志。
[56]   Mutex::Lock lock(mutex_);  —— 加锁，保护共享的线程池、任务队列等状态。
[57]   if (isStopped_) {          —— 如果已处于“停止”状态，直接返回（幂等保护）。
[58]     打印“scheduler has stopped”日志并返回。
[60]   }
[61]   CondPanic(threadPool_.empty(), "thread pool is not empty"); —— 断言启动前线程池应为空（防止误重复启动）。
[62]   threadPool_.resize(threadCnt_); —— 预分配容器容量以存放即将创建的线程对象。
[63]   for (size_t i = 0; i < threadCnt_; i++) { —— 逐个创建工作线程：
[64]     threadPool_[i].reset(new Thread(std::bind(&Scheduler::run, this), name_ + "_" + std::to_string(i)));
         —— 每个线程的入口函数都是 Scheduler::run，线程名称带序号后缀。
[65]     threadIds_.push_back(threadPool_[i]->getId()); —— 记录每个工作线程的线程 ID。
[66]   }
[67] }

[70] void Scheduler::run() {     —— 调度循环：在线程/调度协程中不断从任务队列取任务并执行。
[71]   打印“begin run”。
[72]   set_hook_enable(true);     —— 打开 Hook：让阻塞 I/O 变为“注册事件 + yield”，事件就绪后再 resume。
[73]   setThis();                 —— 将当前线程的“当前调度器”绑定为 this（线程局部变量）。
[74]   if (GetThreadId() != rootThread_) { —— 如果不是 caller 线程：
[75]     cur_scheduler_fiber = Fiber::GetThis().get(); —— 初始化本线程的“调度主协程”，作为协程切换的落点。
[76]   }
[78]   Fiber::ptr idleFiber(new Fiber(std::bind(&Scheduler::idle, this))); —— 创建 idle 协程：没有任务时就切到它，它内部不断 yield。
[79]   Fiber::ptr cbFiber;        —— 复用的临时 Fiber，用来包装“回调任务”（std::function<void()>）。
[81]   SchedulerTask task;        —— 任务槽：可装“协程任务”或“回调任务”。
[82]   while (true) {             —— 主循环：不停取任务并执行。
[83]     task.reset();            —— 清空任务槽。
[84]     bool tickle_me = false;  —— 是否需要“提醒(tickle)”其他线程来抢任务。
[86]     Mutex::Lock lock(mutex_);—— 加锁访问任务队列。
[87]     auto it = tasks_.begin();—— 从队头开始找任务。
[88]     while (it != tasks_.end()) { —— 遍历任务队列：
[89]       if (it->thread_ != -1 && it->thread_ != GetThreadId()) { —— 任务指定了要在某个特定线程上运行，但不是当前线程：
[90]         ++it;                —— 跳过该任务。
[91]         tickle_me = true;    —— 提醒目标线程来处理它。
[92]         continue;            —— 继续看下一个任务。
[93]       }
[94]       CondPanic(it->fiber_ || it->cb_, "task is nullptr"); —— 保证任务要么携带协程，要么携带回调。
[95]       if (it->fiber_) {
[96]         CondPanic(it->fiber_->getState() == Fiber::READY, "fiber task state error"); —— 协程必须处于 READY 才能被调度执行。
[97]       }
[98]       task = *it;            —— 选定任务。
[99]       tasks_.erase(it++);    —— 将任务从队列移除。
[100]      ++activeThreadCnt_;    —— 活动线程计数 +1（表示本线程正忙于执行任务）。
[101]      break;                 —— 结束查找。
[103]     }
[103]     tickle_me |= (it != tasks_.end()); —— 如果队列里还有剩余任务，顺带提醒其他线程。
[105]   if (tickle_me) {
[106]     tickle();               —— 提醒（当前实现仅打印，占位）。
[107]   }
[109]   if (task.fiber_) {        —— 如果拿到的是“协程任务”：
[110]     task.fiber_->resume();  —— 切换到该协程执行（运行到 yield 或结束返回）。
[111]     --activeThreadCnt_;     —— 协程返回后，本线程活动数 -1。
[112]     task.reset();           —— 清理任务槽。
[113]   } else if (task.cb_) {    —— 如果拿到的是“回调任务”：
[114]     if (cbFiber) {
[115]       cbFiber->reset(task.cb_); —— 复用已有 Fiber 对象，仅重置入口回调函数。
[116]     } else {
[117]       cbFiber.reset(new Fiber(task.cb_)); —— 创建新的 Fiber 来承载回调函数。
[118]     }
[119]     task.reset();           —— 清理任务槽。
[120]     cbFiber->resume();      —— 运行回调协程。
[121]     --activeThreadCnt_;     —— 返回后活动数 -1。
[122]     cbFiber.reset();        —— 释放/清空复用指针（当前实现选择释放）。
[123]   } else {                  —— 没有可执行的任务：
[124]     if (idleFiber->getState() == Fiber::TERM) { —— 如果 idle 协程已经结束，说明调度器该退出了。
[125]       打印 "idle fiber term" 并 break。
[126]     }
[128]     ++idleThreadCnt_;       —— 统计：进入空闲状态。
[129]     idleFiber->resume();    —— 切到 idle 协程；它内部会不断 yield，等待新任务到来。
[130]     --idleThreadCnt_;       —— 返回：离开空闲状态。
[131]   }
[132] }
[133] 打印 "run exit"：调度循环结束（线程/协程即将退出）。

[136] void Scheduler::tickle() { std::cout << "tickle" << std::endl; } —— 提醒其他线程有任务可抢（占位实现：仅打印）。

[138] bool Scheduler::stopping() {
[139]   Mutex::Lock lock(mutex_); —— 保护共享状态（任务队列、计数等）。
[140]   return isStopped_ && tasks_.empty() && activeThreadCnt_ == 0; —— 停止条件：收到 stop 且任务队列为空且无活动线程。
[141] }

[143] void Scheduler::idle() {   —— idle 协程：空转等待；没有任务时被 run() 切进来。
[144]   while (!stopping()) {
[145]     Fiber::GetThis()->yield(); —— 主动让出执行权；等待被 tickle/新任务唤醒后再次被切入。
[146]   }
[147] }

[150] void Scheduler::stop() {   —— 发起停止：设置停止标志、唤醒各线程、等待它们退出（join）。
[151]   打印 "stop"。
[152]   if (stopping()) return;   —— 如果已经满足停止条件，直接返回（幂等）。
[155]   isStopped_ = true;        —— 设置停止标志位。
[157]   if (isUseCaller_) {
[158]     CondPanic(GetThis() == this, "cur thread is not caller thread"); —— use_caller 模式下，要求由 caller 线程发起 stop。
[159]   } else {
[160]     CondPanic(GetThis() != this, "cur thread is caller thread"); —— 非 use_caller 模式下，当前线程不应是 caller 线程绑定的调度器。
[161]   }
[163]   for (size_t i = 0; i < threadCnt_; i++) tickle(); —— 提醒所有工作线程尽快从 idle 中返回，完成退出流程。
[166]   if (rootFiber_) tickle(); —— 如果有 caller 线程的调度协程，也提醒它。
[170]   if (rootFiber_) {
[171]     rootFiber_->resume();   —— 切到 caller 线程的调度协程，让它把剩余流程跑完并返回到 caller 主协程。
[172]     打印 "root fiber end"。
[173]   }
[175]   std::vector<Thread::ptr> threads; —— 准备把线程池搬到本地变量，便于逐个 join。
[177]   { Mutex::Lock lock(mutex_); threads.swap(threadPool_); } —— 在锁保护下交换，清空成员中的线程池。
[180]   for (auto &i : threads) i->join(); —— 逐个等待工作线程退出，确保资源回收干净。
[183] } —— stop 结束。

[185] } // namespace monsoon —— 命名空间结束。
*/